#pragma once
#include<pthread.h>
#include<queue>
#include<string>
#include<iostream>

const int SIZE = 30;
template <class T>//阻塞队列是任意类型
class BlockQueue
{
public:
    BlockQueue(int cap_size = SIZE)
    :_cap_size(cap_size)
    {
        //初始化锁
        pthread_mutex_init(&_mutex, nullptr);  
        //初始化_p_cond条件变量
        pthread_cond_init(&_p_cond, nullptr); 
        //初始化_c_cond条件变量 
        pthread_cond_init(&_c_cond, nullptr); 
    }
    //判断阻塞队列是否为空
    bool IfEmpty()
    {
        return _block_queue.empty();
    }
    //判断阻塞队列是否是满的
    bool IfFull()
    {
        return _block_queue.size() == _cap_size;
    }
    //从阻塞队列读取任务/数据
    void Pop(T& out)
    {
        pthread_mutex_lock(&_mutex); 
        while(IfEmpty())
        {
            pthread_cond_wait(&_c_cond, &_mutex);
        }
        out = _block_queue.front();//取队列的头部数据
        _block_queue.pop();
        //唤醒_p_cond条件变量下的一个生产线程
        pthread_cond_signal(&_p_cond);
        pthread_mutex_unlock(&_mutex);
    }
    //向阻塞队列添加任务/数据
    void Enqueue(const T& in)
    {
        //如果阻塞队列满了，进入到消费线程的条件变量中等待，
        //等待消费线程唤醒该生产线程(唤醒条件：有消费线程读取了数据，阻塞队列有空间了)
        //_mutex锁进行加锁
        pthread_mutex_lock(&_mutex); 
        while(IfFull())//这里while循环的原因是有这样的情况：阻塞队列满了之后a生产线程到_p_cond条件变量下去等待
                        //然后有一个消费线程读取了数据，唤醒了_p_cond条件变量下所有的线程，此时阻塞队列有0个空间，
                        //b生产线程竞争到了锁，添加1个数据到阻塞队列中，阻塞队列又满了，a线程已经被唤醒，
                        //此时a线程竞争到锁会pthread_cond_wait(&_p_cond, &_mutex);语句处开始继续执行，如果是if就会
                        //执行_block_queue.push(in);语句添加数据/任务
        {
            pthread_cond_wait(&_p_cond, &_mutex);//线程到指定条件变量下去等待
                                                 //还要释放该线程拿到的锁，此时锁还没有释放如果拿着所锁去等待那就造成其他线程都拿不到锁
                                                 //线程被唤醒时先去竞争锁，线程竞争到锁后从该代码处继续执行
        }
        _block_queue.push(in);
        //唤醒_c_cond条件变量下的一个消费线程
        pthread_cond_signal(&_c_cond);
        //_mutex锁进行解锁
        pthread_mutex_unlock(&_mutex);
    }

    ~BlockQueue()
    {
        //销毁锁
        pthread_mutex_destroy(&_mutex);
        //销毁_p_cond条件变量
        pthread_cond_destroy(&_p_cond);
        //销毁_c_cond条件变量 
        pthread_cond_destroy(&_c_cond);
    }

private:
    size_t _cap_size; //阻塞队列最大容量，这样阻塞队列的大小能够由自己来定
    std::queue<T> _block_queue;//阻塞队列 临界资源
    pthread_mutex_t _mutex;//定义锁
    pthread_cond_t _p_cond;//生产线程的条件变量，有两个条件变量为了编写更方便
    pthread_cond_t _c_cond;//消费线程的条件变量
};

